﻿using System;
using System.Collections.Generic;
using System.Globalization;
using AzukiMap.MapSource;
using GeoAPI.Geometries;
using AzukiMap.Layer;

namespace AzukiMap.Request
{
    internal class WMSRequest : ImageRequest
    {
        WMSSource _wmsMapSource;
        int _outputWidth = 0;
        int _outputHeight = 0;

        private readonly NumberFormatInfo _nfi = new NumberFormatInfo() {NumberDecimalSeparator = "."};

        public int OutputWidth
        {
            get { return _outputWidth; }
            set { _outputWidth = value; }
        }

        public int OutputHeight
        {
            get { return _outputHeight; }
            set { _outputHeight = value; }
        }

        public WMSRequest(WMSSource wmsMapSource)
        {
            _wmsMapSource = wmsMapSource;
        }

        public Uri GetUri(IEnvelope extent, int outputWidth, int outputHeight)
        {
            _outputWidth = outputWidth;
            _outputHeight = outputHeight;
            return GetUri(extent);
        }

        public override Uri GetUri(IEnvelope extent)
        {
            string problemParams = checkMandatoryParams();
            if ( problemParams.Length != 0 )
            {
                throw new WmsParamsNotSetException("WMS Params are not set: " + problemParams);
            }

            var headerChar = _wmsMapSource.RequestUri.Contains("?") ? "&" : "?";
            string uriString =
                _wmsMapSource.RequestUri
                + headerChar 
                + "VERSION=" + GetWMSVersionString(_wmsMapSource.Version)
                + "&REQUEST=" + _wmsMapSource.Request
                + "&LAYERS=" + CreateCommaParam(_wmsMapSource.Layers)
                + "&STYLES=" + CreateCommaParam(_wmsMapSource.Styles)
                + "&" + CreateCrsStr() + "=EPSG:" + _wmsMapSource.Srid.ToString()
                + "&BBOX=" + CreateBboxStr(extent)
                + "&WIDTH=" + _outputWidth.ToString()
                + "&HEIGHT=" + _outputHeight.ToString()
                + "&FORMAT=" + WMSImageFormatToString(_wmsMapSource.Format)
                + "&TRANSPARENT=" + _wmsMapSource.Transparent.ToString().ToUpper()
                + "&BGCOLOR=" + _wmsMapSource.Bgcolor.ToString().Replace("#", "")
                + CreateOtherStr()
                ;

            Uri reqUri = new Uri(uriString, UriKind.RelativeOrAbsolute);
            return reqUri;
        }

        string checkMandatoryParams()
        {
            string problemParams = "";
            if (_wmsMapSource.RequestUri == null)
            {
                problemParams += "RequestUri ";
            }
            if (_wmsMapSource.Layers.Count == 0)
            {
                problemParams += "Layers ";
            }
            if (_outputWidth <= 0)
            {
                problemParams += "Width ";
            }
            if (_outputHeight <= 0)
            {
                problemParams += "Height ";
            }
            return problemParams;
        }

        string CreateCommaParam(List<string> paramList)
        {
            string sepStr = "";
            foreach (string p in paramList)
            {
                if (sepStr == "")
                {
                    sepStr = p;
                }
                else
                {
                    sepStr += "," + p;
                }
            }
            return sepStr;
        }

        string CreateBboxStr(IEnvelope extent)
        {
            double minX = extent.MinX;
            double minY = extent.MinY;
            double maxX = extent.MaxX;
            double maxY = extent.MaxY;

            if (_wmsMapSource.BBoxLatLonOrder == LatLonOrder.LON_LAT)
            {
                return
                    minX.ToString(_nfi) + "," +
                    minY.ToString(_nfi) + "," +
                    maxX.ToString(_nfi) + "," +
                    maxY.ToString(_nfi);
            }
            else
            {
                return
                    minY.ToString(_nfi) + "," +
                    minX.ToString(_nfi) + "," +
                    maxY.ToString(_nfi) + "," +
                    maxX.ToString(_nfi);
            }
        }

        // v1.3.1 uses "CRS"
        // v1.1 uses "SRS"
        string CreateCrsStr()
        {
            string crsOrSrs;
            if (_wmsMapSource.Version == WMSVersion.V1_3_0)
            {
                crsOrSrs = "CRS";
            }
            else
            {
                crsOrSrs = "SRS";
            }
            return crsOrSrs;
        }

        string CreateOtherStr()
        {
            string otherStr = "";
            foreach (KeyValuePair<string, string> keyAndValue in _wmsMapSource.OtherParams)
            {
                otherStr += "&" + keyAndValue.Key + "=" + keyAndValue.Value;
            }
            return otherStr;
        }

        public string GetWMSVersionString(WMSVersion version)
        {
            string res = "";
            switch (version)
            {
                case WMSVersion.V1_1:
                    res = "1.1";
                    break;
                case WMSVersion.V1_1_1:
                    res = "1.1.1";
                    break;
                case WMSVersion.V1_3_0:
                    res = "1.3.0";
                    break;
            }
            return res;
        }

        public WMSVersion GetWMSVersionFromString(string versionStr)
        {
            WMSVersion version;
            switch (versionStr)
            {
                case "1.1":
                    version = WMSVersion.V1_1;
                    break;
                case "1.1.1":
                    version = WMSVersion.V1_1_1;
                    break;
                case "1.3.0":
                    version = WMSVersion.V1_3_0;
                    break;
                default:
                    version = WMSVersion.V1_1_1;
                    break;
            }
            return version;
        }

        public string WMSImageFormatToString(RasterFileSourceFormat format)
        {
            string res = "";
            switch (format)
            {
                case RasterFileSourceFormat.JPEG:
                    res = "image/jpeg";
                    break;
                case RasterFileSourceFormat.PNG:
                    res = "image/png";
                    break;
                case RasterFileSourceFormat.PNG24:
                    res = "image/png; mode=24bit";
                    break;
            }
            return res;
        }

        public RasterFileSourceFormat WMSImageFormatFromString(string formatStr)
        {
            RasterFileSourceFormat format;
            formatStr = formatStr.ToUpper();
            switch (formatStr)
            {
                case "JPEG":
                    format= RasterFileSourceFormat.JPEG;
                    break;
                case "PNG":
                    format= RasterFileSourceFormat.PNG;
                    break;
                case "PNG24":
                    format= RasterFileSourceFormat.PNG24;
                    break;
                default:
                    format= RasterFileSourceFormat.PNG;
                    break;
            }
            return format;
        }


    }
}
